#include <storages/postgres/default_command_controls.hpp>

#include <userver/utils/assert.hpp>

USERVER_NAMESPACE_BEGIN

namespace storages::postgres {

DefaultCommandControls::DefaultCommandControls(
    const CommandControl& default_cmd_ctl_src,
    CommandControlByHandlerMap handlers_command_control_src,
    CommandControlByQueryMap queries_command_control)
    : data_(std::make_shared<Data>(default_cmd_ctl_src,
                                   std::move(handlers_command_control_src),
                                   std::move(queries_command_control))) {}

CommandControl DefaultCommandControls::GetDefaultCmdCtl() const {
  UASSERT(data_);
  return data_->default_cmd_ctl.ReadCopy();
}

void DefaultCommandControls::UpdateDefaultCmdCtl(
    const CommandControl& cmd_ctl, detail::DefaultCommandControlSource source) {
  UASSERT(data_);
  {
    if (source == detail::DefaultCommandControlSource::kGlobalConfig &&
        data_->has_user_default_cc)
      return;
    auto reader = data_->default_cmd_ctl.Read();
    if (*reader == cmd_ctl) return;
  }
  auto writer = data_->default_cmd_ctl.StartWrite();
  // source must be checked under lock to avoid races
  switch (source) {
    case detail::DefaultCommandControlSource::kGlobalConfig:
      if (data_->has_user_default_cc) return;
      break;
    case detail::DefaultCommandControlSource::kUser:
      data_->has_user_default_cc = true;
      break;
  }
  *writer = cmd_ctl;
  writer.Commit();
}

OptionalCommandControl DefaultCommandControls::GetHandlerCmdCtl(
    std::string_view path, std::string_view method) const {
  UASSERT(data_);
  auto reader = data_->handlers_command_control.Read();
  return GetHandlerOptionalCommandControl(*reader, path, method);
}

OptionalCommandControl DefaultCommandControls::GetQueryCmdCtl(
    const std::string& query_name) const {
  UASSERT(data_);
  auto reader = data_->queries_command_control.Read();
  return GetQueryOptionalCommandControl(*reader, query_name);
}

void DefaultCommandControls::UpdateHandlersCommandControl(
    CommandControlByHandlerMap&& handlers_command_control) {
  UASSERT(data_);
  {
    auto reader = data_->handlers_command_control.Read();
    if (*reader == handlers_command_control) return;
  }
  data_->handlers_command_control.Assign(std::move(handlers_command_control));
}

void DefaultCommandControls::UpdateQueriesCommandControl(
    CommandControlByQueryMap&& queries_command_control) {
  UASSERT(data_);
  {
    auto reader = data_->queries_command_control.Read();
    if (*reader == queries_command_control) return;
  }
  data_->queries_command_control.Assign(std::move(queries_command_control));
}

DefaultCommandControls::Data::Data(
    const CommandControl& default_cmd_ctl_src,
    CommandControlByHandlerMap&& handlers_command_control_src,
    CommandControlByQueryMap&& queries_command_control_src)
    : default_cmd_ctl(default_cmd_ctl_src),
      handlers_command_control(std::move(handlers_command_control_src)),
      queries_command_control(std::move(queries_command_control_src)) {}

}  // namespace storages::postgres

USERVER_NAMESPACE_END
